//
//  NetworkOperationManager.m
//  OneStoreFramework
//
//  Created by huang jiming on 14-7-22.
//  Copyright (c) 2014年 OneStore. All rights reserved.
//
#import "Network.h"
#import <CoreCode/CoreCode.h>
#import <ReactiveObjC/RACDisposable.h>
#import <ReactiveObjC/NSObject+RACDeallocating.h>

@interface NetworkOperationManager()

@property(nonatomic, strong) NSMutableArray *operationParams;//这里记录operation param，防止其释放，token过期处理会用到operation param
@property(nonatomic, strong) NSMutableArray *batchOperationParams;//批量处理operation param

@property(nonatomic, strong) NSMutableArray *cachedParamsForTokenExpire;//token过期时缓存的所有operation param
@property(nonatomic, strong) NSMutableArray *cachedParamsForSignKeyExpire;//密钥过期时缓存的所有operation param
@property(nonatomic, strong) NSMutableArray *cachedParamsForLaunchFail;//launch接口调用失败时缓存的所有operation param
@property(nonatomic, strong) NSMutableArray *cachedParamsForShowErrorView;//为错误界面缓存的所有operation param
@property(nonatomic, strong) NSMutableArray *cachedParamsForServerError;//为服务器错误缓存的所有operation param

@property(nonatomic, strong) NSString *hostClassName;//宿主名称

@property(nonatomic, assign) BOOL sessionIsInvalid;//session是否无效

@end

@interface NetworkOperationParam ()

#pragma mark - 拼装requestUrl相关
@property(nonatomic, copy) NSString *methodName;                        //方法名

#pragma mark - 接口调用相关
@property(nonatomic, copy) NSString *requestUrl;                        //请求url
@property(nonatomic, assign) ERequestType requestType;                  //请求类型，post还是get方式，默认为post方式
@property(nonatomic, strong) NSDictionary *requestParam;                //参数

#pragma mark - 接口错误相关
@property(nonatomic, assign) BOOL rerunForTokenExpire;                  //是否token过期自动登录后再次执行，默认为NO
@property(nonatomic, assign) BOOL rerunForSignKeyExpire;                //是否重新调用获取密钥接口后再次执行，默认为NO
@property(nonatomic, assign) BOOL rerunForLaunchFail;                   //是否重新调用启动接口后再次执行，默认为NO
@property(nonatomic, copy) NSString *usedToken;                         //记录当前请求使用的token
@property(nonatomic, copy) NSString *usedIp;                            //记录当前请求使用的ip
@property(nonatomic, assign) BOOL needUseIp;                            //是否需要使用ip，默认为NO

#pragma mark - 接口日志相关
@property(nonatomic, assign) NSTimeInterval startTimeStamp;             //接口调用开始时间，精确到ms
@property(nonatomic, assign) NSTimeInterval endTimeStamp;               //接口调用结束时间，精确到ms

@end

@implementation NetworkOperationManager

+ (instancetype)manager {
    return [self managerWithOwner:nil];
}

+ (instancetype)managerWithOwner:(NSObject*)owner {
    NetworkOperationManager *operationManager = [super manager];
    operationManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html", @"application/json", @"application/javascript", @"text/plain", @"text/json", @"application/x-javascript", nil];
    if (owner) {
        operationManager.hostClassName = NSStringFromClass([owner class]);
        //释放处理
        [owner.rac_willDeallocSignal subscribeCompleted:^{
            [operationManager cancelOperationsAndRemoveFromNetworkManager];
        }];
    }
    //缓存策略
    operationManager.requestSerializer.cachePolicy = NSURLRequestUseProtocolCachePolicy;
    static dispatch_once_t onceTokenInManager;
    dispatch_once(&onceTokenInManager, ^{
        [NSURLCache setSharedURLCache:[NetworkURLCache sharedInstance]];
    });
    
    //query格式
    [operationManager.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) {
        return [CoreNetworkQuery queryStringFromParameters:parameters encoding:NSUTF8StringEncoding];;
    }];
    
    //session无效
    __weak NetworkOperationManager *weakManager = operationManager;
    [operationManager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
        __strong NetworkOperationManager *strongManager = weakManager;
        strongManager.sessionIsInvalid = YES;
    }];
    
    return operationManager;
}

static NSInteger failedHTTPSTimes = 0;
static dispatch_semaphore_t semaphore;

+ (BOOL)exceedHTTPSFailedLimits {
    if (!semaphore) {
        semaphore = dispatch_semaphore_create(1);
    }
    
    BOOL result = NO;
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    failedHTTPSTimes++;
    if (failedHTTPSTimes >= NetworkMaxAllowedHTTPSFailedTimes) {
        result = YES;
    }

    dispatch_semaphore_signal(semaphore);

    return result;
}

- (void)dealloc {
    PBLog(@"[%@ call %@ --> %@]", [self class], NSStringFromSelector(_cmd), _hostClassName);
}

#pragma mark - Property
- (NSMutableArray *)operationParams {
    if (_operationParams == nil) {
        _operationParams = @[].mutableCopy;
    }
    return _operationParams;
}

- (NSMutableArray *)batchOperationParams {
    if (_batchOperationParams == nil) {
        _batchOperationParams = @[].mutableCopy;
    }
    return _batchOperationParams;
}

- (NSMutableArray *)cachedParamsForTokenExpire {
    if (_cachedParamsForTokenExpire == nil) {
        _cachedParamsForTokenExpire = @[].mutableCopy;
    }
    return _cachedParamsForTokenExpire;
}

- (NSMutableArray *)cachedParamsForSignKeyExpire {
    if (_cachedParamsForSignKeyExpire == nil) {
        _cachedParamsForSignKeyExpire = @[].mutableCopy;
    }
    return _cachedParamsForSignKeyExpire;
}

- (NSMutableArray *)cachedParamsForLaunchFail {
    if (_cachedParamsForLaunchFail == nil) {
        _cachedParamsForLaunchFail = @[].mutableCopy;
    }
    return _cachedParamsForLaunchFail;
}

- (NSMutableArray *)cachedParamsForShowErrorView {
    if (_cachedParamsForShowErrorView == nil) {
        _cachedParamsForShowErrorView = @[].mutableCopy;
    }
    return _cachedParamsForShowErrorView;
}

- (NSMutableArray *)cachedParamsForServerError {
    if (_cachedParamsForServerError == nil) {
        _cachedParamsForServerError = @[].mutableCopy;
    }
    return _cachedParamsForServerError;
}

#pragma mark - API
/**
 *  功能:发送请求
 */
- (NSURLSessionDataTask *)requestWithParam:(NetworkOperationParam *)aParam {
    if (aParam == nil) {
        return nil;
    }
    
    //session无效
    if (self.sessionIsInvalid) {
        PBLog(@"{method name}: %@\n\nerror:\nsession invalid\n", aParam.methodName);
        PBLog(@"(%lld)接口session invalid\n接口名%@\n\n", (long long)([[NetworkConfig sharedInstance].serverTime timeIntervalSince1970]*1000), aParam.requestUrl);

        dispatch_async(dispatch_get_main_queue(), ^{
            NSError *error = [NSError errorWithDomain:@"session invalid" code:kSessionInvalid userInfo:nil];
            if (aParam.isNeedRacRequest) {
                [aParam.subscriber sendError:error];
            } else if (aParam.callbackBlock) {
                aParam.callbackBlock(nil, error);
            }
            [self.operationParams removeObject:aParam];
        });
        return nil;
    }
    
    //启动接口调用失败
    if (aParam.needSignature && [NetworkConfig sharedInstance].signatureKey == nil) {
//        BOOL runCallBack = [[NetworkLaunchFailError sharedInstance] dealWithManager:self param:aParam operation:nil responseObject:nil error:nil];
        PBLog(@"{method name}: %@\n\nerror:\nlaunch fail\n", aParam.methodName);
        PBLog(@"(%lld)接口launch fail\n接口名%@\n\n", (long long)([[NetworkConfig sharedInstance].serverTime timeIntervalSince1970]*1000), aParam.requestUrl);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSError *error = [NSError errorWithDomain:@"launch fail" code:kLaunchFail userInfo:nil];
            if (aParam.isNeedRacRequest) {
                [aParam.subscriber sendError:error];
            } else if (aParam.callbackBlock) {
                aParam.callbackBlock(nil, error);
            }
            [self.operationParams removeObject:aParam];
        });
        return nil;
    }
    
    //将operation param加入数组，便于管理
    if (aParam) {
        [self.operationParams addObject:aParam];
    }
    
    //requestSerializer处理
    [self modifyRequestSerializerWithParam:aParam];
    
    //业务参数组装
    NSMutableDictionary *params = [NetworkRequestResponse demandParam:aParam].mutableCopy;
    
    //http dns
    NSString *requestUrl = aParam.requestUrl;
    
    //接口请求
    NSURLSessionDataTask *requestOperation = nil;
    WEAK_SELF;
    if (aParam.requestType == kRequestPost) {//POST方式
//        requestOperation = [self POST:requestUrl parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
//            STRONG_SELF;
//            [self successWithTask:task responseObject:responseObject param:aParam];
//        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
//            STRONG_SELF;
//            [self failWithTask:task error:error param:aParam];
//        }];
    } else {
//        requestOperation = [self GET:requestUrl parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
//        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
//            STRONG_SELF;
//            [self successWithTask:task responseObject:responseObject param:aParam];
//        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
//            STRONG_SELF;
//            [self failWithTask:task error:error param:aParam];
//        }];
    }
    
    //记录当前使用的token，便于token过期的判断处理
    aParam.usedToken = [NetworkConfig sharedInstance].token;
    
    //记录接口开始调用时间，便于统计接口耗时
    aParam.startTimeStamp = [[NSDate date] timeIntervalSince1970] * 1000;
    
    return requestOperation;
}
/**
 *  发送网络请求，创建信号
 */
- (RACSignal *)rac_requestWithParam:(NetworkOperationParam *)aParam {
   return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       aParam.subscriber = subscriber;
       aParam.isNeedRacRequest = YES;
       NSURLSessionDataTask *task = [self requestWithParam:aParam];
       return [RACDisposable disposableWithBlock:^{
            [task cancel];
       }];
    }];
}

/**
 *  功能:取消当前manager queue中所有网络请求
 */
- (void)cancelAllOperations {
    NSArray *tasks = self.tasks.copy;
    for (NSURLSessionDataTask *task in tasks) {
        [task cancel];
    }
    
    //call back block置空
    for (NetworkOperationParam *operationParam in self.operationParams.copy) {
        operationParam.callbackBlock = nil;
        operationParam.retryTimes = 0;
    }
    [self.operationParams removeAllObjects];
    
    for (BatchOperaionParam *batch in self.batchOperationParams.copy) {
        batch.completeBlock = nil;
    }
    [self.batchOperationParams removeAllObjects];
    
    for (NetworkOperationParam *operationParam in self.cachedParamsForTokenExpire.copy) {
        operationParam.callbackBlock = nil;
        operationParam.retryTimes = 0;
    }
    [self.cachedParamsForTokenExpire removeAllObjects];
    
    for (NetworkOperationParam *operationParam in self.cachedParamsForSignKeyExpire.copy) {
        operationParam.callbackBlock = nil;
        operationParam.retryTimes = 0;
    }
    [self.cachedParamsForSignKeyExpire removeAllObjects];
    
    for (NetworkOperationParam *operationParam in self.cachedParamsForLaunchFail.copy) {
        operationParam.callbackBlock = nil;
        operationParam.retryTimes = 0;
    }
    [self.cachedParamsForLaunchFail removeAllObjects];
    
    for (NetworkOperationParam *operationParam in self.cachedParamsForShowErrorView.copy) {
        operationParam.callbackBlock = nil;
        operationParam.retryTimes = 0;
    }
    [self.cachedParamsForShowErrorView removeAllObjects];
    
    for (NetworkOperationParam *operationParam in self.cachedParamsForServerError.copy) {
        operationParam.callbackBlock = nil;
        operationParam.retryTimes = 0;
    }
    [self.cachedParamsForServerError removeAllObjects];
}

#pragma mark - Success & Fail
/**
 *  功能:接口调用成功处理
 */
- (void)successWithTask:(NSURLSessionDataTask *)task
         responseObject:(id)responseObject
                  param:(NetworkOperationParam *)aParam {
    failedHTTPSTimes = 0;
        
    //数据的二次加工
    [NetworkRequestResponse successWithTask:task responseObject:responseObject param:aParam];
        
    [self.operationParams removeObject:aParam];
}

/**
 *  功能:接口调用失败处理
 */
- (void)failWithTask:(NSURLSessionDataTask *)task
               error:(NSError *)aError
               param:(NetworkOperationParam *)aParam {
    
    [NetworkRequestResponse failWithTask:task error:aError param:aParam];
    
    if (!aParam.shouldRetry) {
        [self.operationParams removeObject:aParam];
    } else {
        [self retryWithOperationParam:aParam];
    }
}

- (void)retryWithOperationParam:(NetworkOperationParam *)operationParam {
    //接口重试
    operationParam.retriedTimes++;
    [self requestWithParam:operationParam];
}

#pragma mark -- Inner
/**
 *  功能:修改RequestSerializer
 */
- (void)modifyRequestSerializerWithParam:(NetworkOperationParam *)aParam {
    //超时时间
    self.requestSerializer.timeoutInterval = aParam.timeoutTime;
    
    //Cache-Control(缓存时间)
    [self.requestSerializer setValue:[NSString stringWithFormat:@"max-age=%.0f", aParam.cacheTime] forHTTPHeaderField:@"Cache-Control"];
    [self.requestSerializer setValue:@"gzip,deflate" forHTTPHeaderField:@"Accept-Encoding"];

    //User-Agent
    NSString *userAgent = [NSString stringWithFormat:@"network-%@-%@-%@-(%@; iOS %@)", (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? @"ipad": @"iphone"), ([[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ? : [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey]), ([[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ? : [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey]), [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion]];
    [self.requestSerializer setValue:userAgent forHTTPHeaderField:@"User-Agent"];
    
    //Content-Type
    if (aParam.requestType == kRequestPost) {
        [self.requestSerializer setValue:@"application/x-www-form-urlencoded;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    } else {
        [self.requestSerializer setValue:nil forHTTPHeaderField:@"Content-Type"];
    }
    
    //clientInfo
    NSString *clientInfo = [[NetworkConfig sharedInstance] toJSONString];
    [self.requestSerializer setValue:clientInfo forHTTPHeaderField:@"clientInfo"];
    
    //userToken
    NSString *token = aParam.needEncoderToken ? [[NetworkConfig sharedInstance] getAesEncodedToken] : [NetworkConfig sharedInstance].token;
    [self.requestSerializer setValue:token forHTTPHeaderField:@"userToken"];
    
    NSDictionary *globalHeaderInfo = [NetworkConfig sharedInstance].addressInfo;
    [globalHeaderInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        if ([obj isKindOfClass:NSNull.class]) {
            [self.requestSerializer setValue:nil forHTTPHeaderField:key];
        } else {
            [self.requestSerializer setValue:obj forHTTPHeaderField:key];
        }
    }];
}

/**
 *  功能:取消当前manager queue中所有网络请求，并从network manager中移除
 */
- (void)cancelOperationsAndRemoveFromNetworkManager {
    [self cancelAllOperations];
    
    self.sessionIsInvalid = YES;
//    [self invalidateSessionCancelingTasks:YES];
    
    [[NetworkManager sharedInstance] removeOperationManger:self];
}

#pragma mark - Token Expire
/**
 *  功能:token过期时将operation暂存
 */
- (void)cacheOperationForTokenExpire:(NetworkOperationParam *)aParam {
    if (!aParam.rerunForTokenExpire) {
        if (aParam) {
            [self.cachedParamsForTokenExpire addObject:aParam];
        }
    }
}

/**
 *  功能:登录成功后执行所有暂存的operation
 */
- (void)performCachedOperationsForTokenExpire {
    NSArray *copyArray = self.cachedParamsForTokenExpire.copy;
    for (NetworkOperationParam *param in copyArray) {
        if (!param.rerunForTokenExpire) {
            param.rerunForTokenExpire = YES;
            [self requestWithParam:param];
        }
    }
    [self.cachedParamsForTokenExpire removeAllObjects];
}

/**
 *  功能:登录失败后清除所有暂存的operation
 */
- (void)clearCachedOperationsForTokenExpire {
    //清除之前先执行call back
    NSArray *copyArray = self.cachedParamsForTokenExpire.copy;
    for (NetworkOperationParam *param in copyArray) {
        if (!param.rerunForTokenExpire) {
            param.rerunForTokenExpire = YES;
            //回调
            if (param.callbackBlock) {
                NSError *error = [NSError errorWithDomain:@"login fail when token expire" code:kLoginFailWhenTokenExpire userInfo:nil];
                param.callbackBlock(nil, error);
            }
        }
        
        //发通知显示错误界面
        [self notifyShowErrorWithParam:param];
    }
    
    [self.cachedParamsForTokenExpire removeAllObjects];
}

#pragma mark - Sign Key Expire
/**
 *  功能:密钥过期时将operation暂存
 */
- (void)cacheOperationForSignKeyExpire:(NetworkOperationParam *)aParam {
    if (!aParam.rerunForSignKeyExpire) {
        if (aParam) {
            [self.cachedParamsForSignKeyExpire addObject:aParam];
        }
        
    }
}

/**
 *  功能:获取密钥接口成功后执行所有暂存的operation
 */
- (void)performCachedOperationsForSignKeyExpire {
    NSArray *copyArray = self.cachedParamsForSignKeyExpire.copy;
    for (NetworkOperationParam *param in copyArray) {
        if (!param.rerunForSignKeyExpire) {
            param.rerunForSignKeyExpire = YES;
            [self requestWithParam:param];
        }
    }
    [self.cachedParamsForSignKeyExpire removeAllObjects];
}

/**
 *  功能:获取密钥接口失败后清除所有暂存的operation
 */
- (void)clearCachedOperationsForSignKeyExpire {
    //清除之前先执行call back
    NSArray *copyArray = self.cachedParamsForSignKeyExpire.copy;
    for (NetworkOperationParam *param in copyArray) {
        if (!param.rerunForSignKeyExpire) {
            param.rerunForSignKeyExpire = YES;
            //回调
            if (param.callbackBlock) {
                NSError *error = [NSError errorWithDomain:@"get sign key fail when sign key expire" code:kGetSignKeyFailWhenSignKeyExpire userInfo:nil];
                param.callbackBlock(nil, error);
            }
        }
        
        //发通知显示错误界面
        [self notifyShowErrorWithParam:param];
    }
    
    [self.cachedParamsForSignKeyExpire removeAllObjects];
}

#pragma mark - Launch Fail
/**
 *  功能:launch接口调用失败时将operation暂存
 */
- (void)cacheOperationForLaunchFail:(NetworkOperationParam *)aParam {
    if (!aParam.rerunForLaunchFail) {
        if (aParam) {
            [self.cachedParamsForLaunchFail addObject:aParam];
        }
        
    }
}

/**
 *  功能:relaunch成功后执行所有暂存的operation
 */
- (void)performCachedOperationsForLaunchFail {
    NSArray *copyArray = self.cachedParamsForLaunchFail.copy;
    for (NetworkOperationParam *param in copyArray) {
        if (!param.rerunForLaunchFail) {
            param.rerunForLaunchFail = YES;
            [self requestWithParam:param];
        }
    }
    [self.cachedParamsForLaunchFail removeAllObjects];
}

/**
 *  功能:relaunch失败后清除所有暂存的operation
 */
- (void)clearCachedOperationsForLaunchFail {
    //清除之前先执行call back
    NSArray *copyArray = self.cachedParamsForLaunchFail.copy;
    for (NetworkOperationParam *param in copyArray) {
        if (!param.rerunForLaunchFail) {
            param.rerunForLaunchFail = YES;
            //回调
            if (param.callbackBlock) {
                NSError *error = [NSError errorWithDomain:@"launch fail" code:kLaunchFail userInfo:nil];
                param.callbackBlock(nil, error);
            }
        }
        
        //发通知显示错误界面
        [self notifyShowErrorWithParam:param];
    }
    
    [self.cachedParamsForLaunchFail removeAllObjects];
}

#pragma mark - Show Error View
/**
 *  功能:发notification，显示错误界面
 */
- (void)notifyShowErrorWithParam:(NetworkOperationParam *)aParam {
    if (aParam.showErrorView) {//显示错误界面
        aParam.blockSendError = YES;
        dispatch_async(dispatch_get_main_queue(), ^{
            //缓存所有出错接口
            if (aParam) {
                [self.cachedParamsForShowErrorView addObject:aParam];
            }
            
            @try {
                [[NSNotificationCenter defaultCenter] postNotificationName:ShowNetworkErrorNotification object:self userInfo:nil];
            }
            @catch (NSException *exception) {}
            @finally {}
        });
    }
}

/**
 *  功能:发notification，隐藏无网络错误界面
 */
- (void)notifyHideNoConnectError {
    //有网络，则隐藏无网络错误界面
    if ([NetworkReachability sharedInstance].currentNetStatus != kConnectToNull) {
        dispatch_async(dispatch_get_main_queue(), ^{
            @try {
                [[NSNotificationCenter defaultCenter] postNotificationName:HideNetworkErrorNotification object:self userInfo:nil];
            }
            @catch (NSException *exception) {}
            @finally {}
        });
    }
}

/**
 *  功能:执行错误界面缓存的operation
 */
- (void)performCachedOperationForShowErrorView {
    NSArray *copyArray = self.cachedParamsForShowErrorView.copy;
    for (NetworkOperationParam *param in copyArray) {
        param.blockSendError = NO;
        [self requestWithParam:param];
    }
    
    [self.cachedParamsForShowErrorView removeAllObjects];
}

#pragma mark - Server Error
/**
 *  功能:服务器错误时将operation暂存
 */
- (void)cacheOperationForServerError:(NetworkOperationParam *)aParam {
    if (aParam) {
        [self.cachedParamsForServerError addObject:aParam];
    }
    
}

/**
 *  功能:服务器错误更换ip后执行所有暂存的operation
 */
- (void)performCachedOperationsForServerError {
    NSArray *copyArray = self.cachedParamsForServerError.copy;
    for (NetworkOperationParam *param in copyArray) {
        param.needUseIp = YES;//使用ip
        [self requestWithParam:param];
    }
    [self.cachedParamsForServerError removeAllObjects];
}

/**
 *  功能:更换ip仍然失败后清除所有暂存的operation
 */
- (void)clearCachedOperationsForServerError {
    //清除之前先执行call back
    NSArray *copyArray = self.cachedParamsForServerError.copy;
    for (NetworkOperationParam *param in copyArray) {
        //重置是否使用ip
        param.needUseIp = NO;
        //重置使用的ip
        param.usedIp = nil;
        
        //回调
        if (param.callbackBlock) {
            NSError *error = [NSError errorWithDomain:@"request fail when change ip for server error" code:kChangeIpForServerError userInfo:nil];
            param.callbackBlock(nil, error);
        }
        
        //发通知显示错误界面
        [self notifyShowErrorWithParam:param];
    }
    
    [self.cachedParamsForServerError removeAllObjects];
}

@end
